package com.hsogoo.study.concurrency;

import sun.misc.Unsafe;

import java.lang.reflect.Constructor;
import java.util.concurrent.locks.LockSupport;

/**
 * 自定义锁，实现简单的加锁，解锁
 */
public class MyLock {

    private volatile Node head;
    private volatile int state = 0;
    private static long headOffset;
    private static long stateOffset;
    private static Unsafe unsafe;

    static {
        try {
            Constructor<Unsafe> constructor = Unsafe.class.getDeclaredConstructor(new Class<?>[0]);
            constructor.setAccessible(true);
            unsafe = constructor.newInstance(new Object[0]);
            stateOffset = unsafe.objectFieldOffset(MyLock.class.getDeclaredField("state"));
            headOffset = unsafe.objectFieldOffset(MyLock.class.getDeclaredField("head"));
        } catch (Exception e) {
        }
    }

    public void lock() {
        if (compareAndSetState(0, 1)) {
            return;
        } else {
            addNodeAndWait();
        }
    }

    public void unlock(){
        compareAndSetState(1, 0);
        Thread thread = pop();
        if(thread != null){
            LockSupport.unpark(thread);
        }
    }

    private void addNodeAndWait() {
        //如果当前只有一个等待线程时，重新获取一下锁，防止永远不被唤醒。
        boolean isOnlyOne = getHead() == null ? true : false;
        //加入等待队列
        push(Thread.currentThread());
        if (isOnlyOne && compareAndSetState(0, 1)) {
            return;
        }
        LockSupport.park(this);//线程被挂起
        if (compareAndSetState(0, 1)) {//线程被唤醒后继续竞争锁
            return;
        } else {
            addNodeAndWait();
        }
    }

    private void push(Thread thread) {
        Node node = new Node(thread);
        for (; ; ) {
            Node first = getHead();
            node.setNext(first);
            if (compareAndSetHead(first, node)) {
                return;
            }
        }
    }

    private Thread pop() {
        Node first = null;
        for (; ; ) {
            first = getHead();
            Node next = null;
            if (first != null) {
                next = first.getNext();
            }
            if (compareAndSetHead(first, next)) {
                break;
            }
        }
        return first == null ? null : first.getThread();
    }

    private boolean compareAndSetHead(Node expect, Node update) {
        return unsafe.compareAndSwapObject(this, headOffset, expect, update);
    }

    private Node getHead() {
        return this.head;
    }

    private boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }


    private class Node {

        volatile Node next;
        volatile Thread thread;

        public Node(Thread thread) {
            this.thread = thread;
        }

        public void setNext(Node next) {
            this.next = next;
        }

        public Node getNext() {
            return next;
        }

        public Thread getThread() {
            return this.thread;
        }
    }


}
